package net.sf.odinms.exttools.doclet.pages;

import java.io.IOException;
import java.util.List;
import com.sun.javadoc.*;
import com.sun.tools.doclets.internal.toolkit.util.Util;
import net.sf.odinms.exttools.doclet.AbstractPageWriter;

/**
 * Contains functionality common to symbol index generation classes.
 */
@SuppressWarnings("unchecked")
public abstract class AbstractIndex extends AbstractPageWriter {
	
	/**
	 * Generate the Javadoc index, specified by user as either single or split.
	 * If {@link net.sf.odinms.exttools.doclet.Config#createindex} is false, no
	 * index is created. Creates a single page unless
	 * {@link net.sf.odinms.exttools.doclet.Config#splitindex} is true.
	 */
	public static void generateIndex() {
		if (CONF.createindex) {
			if (CONF.splitindex)
				SplitIndex.generateSplitIndexPages();
			else
				SingleIndex.generateSingleIndexPage();
		}
	}
	
	/**
	 * Initialize filename and page type for unified index page.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	protected AbstractIndex(String filename) throws IOException {
		this(filename, "");
	}
	
	/**
	 * Initialize filename and page type for split index pages.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @param pathToFile The path from the document root to this file.
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	protected AbstractIndex(String filename, String pathToFile)
			throws IOException {
		super(filename, pathToFile);
		pageType = PageType.INDEX;
	}
	
	/**
	 * Generate the member information for the unicode character along with the
	 * list of the members.
	 * 
	 * @param unicode Unicode for which member list information to be generated.
	 * @param memberlist List of members for the unicode character.
	 */
	protected void characterList(Character unicode, List memberlist) {
		String letter = unicode.toString();
		println(new TagBuilder("h2").add("id", letter).getOpenTextClose(letter));
		
		println(open("dl"));
		for (int i = 0; i < memberlist.size(); i++) {
			Doc element = (Doc) memberlist.get(i);
			if (element instanceof ExecutableMemberDoc) {
				printDescription((ExecutableMemberDoc) element);
			} else if (element instanceof FieldDoc) {
				printDescription((FieldDoc) element);
			} else if (element instanceof ClassDoc) {
				printDescription((ClassDoc) element);
			} else if (element instanceof PackageDoc) {
				printDescription((PackageDoc) element);
			}
		}
		println(close("dl"));
	}
	
	// TODO Consider printing package (and class) in DT as well.
	
	/**
	 * Print one line summary comment for the package.
	 * 
	 * @param pkg The PackageDoc for which to create an entry.
	 */
	private void printDescription(PackageDoc pkg) {
		printDTWithClass(linkToLabelHrefTargetTitle(pkg.name(), hrefToDoc(pkg)
				+ "package-summary" + CONF.ext, null, "Package " + pkg.name()),
				"package");
		printDD(getCommentSummary(pkg));
	}
	
	/**
	 * Print one line summary comment for the class.
	 * 
	 * @param cd The ClassDoc for which to create an entry.
	 */
	private void printDescription(ClassDoc cd) {
		printDTWithClass(linkToLabelHrefTargetTitle(cd.name(), hrefToDoc(cd),
				null, classFlavor(cd, false) + " in package "
						+ cd.containingPackage()), visibilityModifier(cd)
				+ classFlavor(cd, true) + deprecationTag(cd));
		printDD(getCommentSummary(cd));
	}
	
	/**
	 * Create description for a Constructor, Method, or Annotation Type.
	 * 
	 * @param doc The ExecutableMemberDoc for which to create an entry.
	 */
	private void printDescription(ExecutableMemberDoc doc) {
		String linkTitle =
				(doc instanceof MethodDoc)
						? "Method"
						: (doc instanceof ConstructorDoc)
								? "Constructor"
								: "Annotation type";
		linkTitle += " in " + containerTypeAndName(doc);
		String linkClass =
				(doc instanceof MethodDoc)
						? "method"
						: (doc instanceof ConstructorDoc)
								? "constructor"
								: "annotation_type_element";
		
		printDTWithClass(linkToLabelHrefTargetTitle(doc.name()
				+ getJoinedParameters(doc, false, false, false),
				hrefToDoc(doc), null, linkTitle), visibilityModifier(doc)
				+ linkClass + deprecationTag(doc));
		printDD(getCommentSummary(doc));
	}
	
	/**
	 * Create description for a Field.
	 * 
	 * @param doc The FieldDoc for which to create an entry.
	 * @see com.sun.javadoc.FieldDoc
	 */
	private void printDescription(FieldDoc doc) {
		String name =
				(doc instanceof ExecutableMemberDoc)
						? doc.name()
								+ ((ExecutableMemberDoc) doc).flatSignature()
						: doc.name();
		if (name.indexOf("<") != -1 || name.indexOf(">") != -1) {
			name = Util.escapeHtmlChars(name);
		}
		printDTWithClass(linkToLabelHrefTargetTitle(doc.name(), hrefToDoc(doc),
				null, "Field in " + containerTypeAndName(doc)),
				visibilityModifier(doc) + "field" + deprecationTag(doc));
		printDD(getCommentSummary(doc));
	}
	
	/**
	 * Create text for deprecated parameter objects. Used for creating the
	 * correct sequence of CSS tags for an element.
	 * 
	 * @param doc The {@link ProgramElementDoc} to test for deprecation.
	 * @return " deprecated" if true, "" otherwise.
	 */
	private String deprecationTag(ProgramElementDoc doc) {
		return (Util.isDeprecated(doc)) ? " deprecated" : "";
	}
	
	/**
	 * Create text for the most immediate containing program element.
	 * 
	 * @param doc The {@link MemberDoc} for which to find container information.
	 * @return A string with the containing class "flavor" (class, interface,
	 *         enum, etc.) and the name of that containing entity.
	 */
	private String containerTypeAndName(MemberDoc doc) {
		ClassDoc container = doc.containingClass();
		return classFlavor(container, true) + " " + container.toString();
	}
	
	/** Highlight "Index" as current section, don't create link. */
	protected void navLinkIndex() {
		println(listItemCurrent(CONF.propertyText("Index")));
	}
	
}
